package com.sqxgg.drugstore.autoSql.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sqxgg.drugstore.autoSql.annotation.*;
import com.sqxgg.drugstore.autoSql.tools.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.apache.commons.beanutils.PropertyUtils;
import com.sqxgg.drugstore.autoSql.annotation.Column.Action;
import static com.sqxgg.drugstore.autoSql.annotation.DatabaseTime.Operate;
public class BaseEntity {
    @Transient
    protected String foreignKey;//外键,一对多操作时候使用,
    @Column(name = "create_by", data = {"createBy.id", "createBy.name"})
    protected Operator createBy; // 创建者
    @Column(name = "update_by", data = {"updateBy.id", "updateBy.name"})
    protected Operator updateBy; // 更新者
    @DatabaseTime(operate = {Operate.INSERT})
    protected Date createDate; // 创建日期
    @DatabaseTime(operate = {Operate.INSERT, Operate.UPDATE})
    protected Date updateDate; // 更新日期


    public Operator getCreateBy() {
        return createBy;
    }

    public void setCreateBy(Operator createBy) {
        this.createBy = createBy;
    }

    public Operator getUpdateBy() {
        return updateBy;
    }

    public void setUpdateBy(Operator updateBy) {
        this.updateBy = updateBy;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }

    public String getForeignKey() {
        return foreignKey;
    }

    public void setForeignKey(String foreignKey) {
        this.foreignKey = foreignKey;
    }

    /**
     * 生成新增sql
     *
     * @return
     */
    @JsonIgnore
    public String getInsertSql() {
        AttrTable table = attrTable(Action.SAVE);
        StringBuilder sql = new StringBuilder();
        StringBuilder columns = new StringBuilder();
        StringBuilder values = new StringBuilder();
        for (AttrField f : table.getColumns()) {
            columns.append(f.getColumn()).append(",");
            // 如果使用数据库时间作为审计字段的时间
            if (inOperatesArray(f.getOperates(), DatabaseTime.Operate.INSERT)) {
                values.append("now(),");
            } else {
                values.append("#{").append(f.getField()).append("},");
            }

        }
        sql.append("INSERT INTO ").append(table.getTableName()).append(" ( ");
        sql.append(StringUtils.removeEnd(columns.toString(), ","));
        sql.append(" ) VALUES ( ");
        sql.append(StringUtils.removeEnd(values.toString(), ",")).append(")");
        return sql.toString();
    }
    /**
     * 生成updatesql
     *
     * @return
     */
    @JsonIgnore
    public String getUpdateSql() {
        AttrTable table = attrTable(Action.UPDATE);
        StringBuilder sql = new StringBuilder();
        StringBuilder columns = new StringBuilder();

        for (AttrField f : table.getColumns()) {
            // 修改动作忽略createDate,createBy
            if (StringUtils.equals("createBy.id", f.getField()) || StringUtils.equals("createDate", f.getField())) {
                continue;
            }
//            // 针对于OneToMany的子表数据修改,外键的值从foreignValue中获取
//            if (StringUtils.equals(f.getColumn(), getForeignKey())) {
//                f.setField("foreignValue");
//            }
//            // 如果使用数据库时间作为审计字段的时间
//            if (inOperatesArray(f.getOperates(), Operate.UPDATE)) {
//                columns.append(f.getColumn()).append(" = ").append("now(),");
//            } else {
//                columns.append(f.getColumn()).append(" = ").append("#{").append(f.getField()).append("},");
//            }
            columns.append(f.getColumn()).append(" = ").append("#{").append(f.getField()).append("},");
        }
        sql.append("UPDATE ").append(table.getTableName()).append(" SET ");
        sql.append(StringUtils.removeEnd(columns.toString(), ","));
        sql.append(" WHERE ").append(table.getPrimaryColumn()).append("=").append("#{").append(table.getPrimaryField()).append("}");
        return sql.toString();
    }
    /**
     * 生成Getsql
     *
     * @return
     */
    @JsonIgnore
    public String getGetSql() {
        AttrTable table = attrTable(Action.QUERY);
        List<JoinTable> joinTables = joinTable();
        StringBuilder sql = new StringBuilder();
        StringBuilder columns = new StringBuilder();

        for (AttrField f : table.getColumns()) {
            if (!f.isIgnoreAlias()) {
                columns.append("a.");
            }
            columns.append(f.getColumn()).append(" AS ").append("\"").append(f.getField()).append("\",");
        }
        if (!joinTables.isEmpty()) {
            for (JoinTable jt : joinTables) {
                for (AttrField f : jt.getColumns()) {
                    columns.append(f.getColumn()).append(" AS ").append("\"").append(f.getField()).append("\",");
                }
            }
        }
        sql.append("SELECT ");
        sql.append(StringUtils.removeEnd(columns.toString(), ","));
        sql.append(" FROM ").append(table.getTableName()).append(" a");
        if (!joinTables.isEmpty()) {
            for (JoinTable jt : joinTables) {
                sql.append(" ").append(jt.getJoin()).append(" ").append(jt.getTableName()).append(" ").append(jt.getAlias())
                        .append(" ON ").append(jt.getFkAlias()).append(".").append(jt.getFk()).append(" = ").append(jt.getAlias()).append(".id ");
                if (StringUtils.isNotBlank(jt.getConditions())) {
                    sql.append(" ").append(jt.getConditions());
                }
            }
        }
        sql.append(" WHERE a.").append(table.getPrimaryColumn()).append("=").append("#{").append(table.getPrimaryField()).append("}");
        return sql.toString();
    }

    /**
     * 生成当前entity对应的表的基本信息以及所有字段
     *
     * @param action 操作类型
     * @return
     */
    protected AttrTable attrTable(Action action) {
        AttrTable table = new AttrTable();
        //默认使用类名转小写作为表名
        String tableName;
        //获取所有的字段(包含父类)
        Class clazz = getClass();
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            fields.addAll(new ArrayList<Field>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Table tableAnnotation = getClass().getAnnotation(Table.class);
        // 如果设置了table名称,则使用设置的
        if (tableAnnotation != null && StringUtils.isNotBlank(tableAnnotation.name())) {
            tableName = tableAnnotation.name();
        } else {//如果没设置,则使用entity的类名进行驼峰转换成表名
            tableName = StringUtils.toUnderScoreCase(getClass().getSimpleName());
        }
        table.setTableName(tableName);
        table.setPrimaryColumn("id");
        table.setPrimaryField("id");
        // 如果不需要字段,直接返回
        if (Action.NULL.equals(action)) {
            return table;
        }
        for (Field field : fields) {
            // 跳过静态属性
            String mod = Modifier.toString(field.getModifiers());
            if (mod.indexOf("static") != -1) {
                continue;
            }
            // 是否忽略当前字段的存储
            Transient transientAnnotation = field.getAnnotation(Transient.class);
            // 为空表示需要存储当前字段
            if (transientAnnotation == null || transientAnnotation.ifDataIsNull()) {
                String columnName;
                Object columnData = null;
                String fieldName = field.getName();
                //查看是否定义了列属性
                Column columnAnnotation = field.getAnnotation(Column.class);

                // 如果设置了fkTable表示这个字段仅仅是用来做joinTable的关联查询属性,不作为数据库存储字段
                if (columnAnnotation != null && columnAnnotation.fkTable().length > 0) {
                    continue;
                }

                Action columnAction = columnAnnotation == null ? Action.ALL : columnAnnotation.action();
                boolean ignoreAlias = columnAnnotation == null ? false : columnAnnotation.ignoreAlias();
                if (!Action.ALL.equals(action)) {
                    if (!Action.ALL.equals(columnAction)) {
                        if (!columnAction.equals(action)) {
                            continue;
                        }
                    }
                }
                // 如果定义了列属性,则使用定义的列属性作为column
                if (columnAnnotation != null && StringUtils.isNotBlank(columnAnnotation.name())) {
                    columnName = columnAnnotation.name();
                } else { // 否则使用当前字段名称进行驼峰转换作为column
                    columnName = StringUtils.toUnderScoreCase(fieldName);
                }

                try {
                    // 获取属性类型
                    Class<?> typeClass = field.getType();
                    //实例化
                    Object typeClassInstance = null;
                    //先从entity中获取当前属性的值,如果能获取到,那么直接使用当前值
                    Object data = PropertyUtils.getProperty(this, fieldName);
                    if (data != null) {
                        typeClassInstance = data;
                    } else {
                        // 如果获取不到属性值,则需要通过反射实例化一个类型的对象,来判断属性的是什么类型
                        try {
                            typeClassInstance = typeClass.newInstance();
                        } catch (Exception e) {
                        }
                    }
                    // 如果当前属性返回的数据是BaseEntity类型,即该字段是一个对象,那么默认取当前对象的id属性的值
                    if (typeClassInstance != null && typeClassInstance instanceof BaseEntity) {
                        // 如果是对象类型,并且没有设置columnAnnotation.name属性,则column应该在后面+_id
                        if (columnAnnotation == null || StringUtils.isBlank(columnAnnotation.name())) {
                            columnName = columnName + "_id";
                        }
                        // 如果定义了取数据的属性,则从定义中取值,

                        if (columnAnnotation != null && columnAnnotation.data() != null && columnAnnotation.data().length > 0) {
                            if (data != null) {
                                columnData = PropertyUtils.getProperty(this, columnAnnotation.data()[0]);
                            }
                            fieldName = columnAnnotation.data()[0];
                        } else {//否则取默认的id属性
                            if (data != null) {
                                columnData = PropertyUtils.getProperty(data, "id");
                            }
                            fieldName = fieldName + ".id";
                        }
                    } else {//否则直接读取当前属性的值
                        columnData = data;
                    }
                    if (transientAnnotation != null && transientAnnotation.ifDataIsNull() && columnData == null) {

                    } else {
                        // 设置主键
                        if (StringUtils.equals("id", fieldName)) {
                            table.setPrimaryColumn("id");
                            table.setPrimaryField("id");
                            table.setPrimaryData(columnData);
                        }
                        AttrField attrField = new AttrField(ignoreAlias, fieldName, columnName, columnData);
                       //  使用数据库时间
                        DatabaseTime databaseTime = field.getAnnotation(DatabaseTime.class);
                        attrField.setOperates(databaseTime.operate());
                        table.getColumns().add(attrField);
                    }
                } catch (Exception e) {
                    // 忽略没有get,set的属性,否则会报错
                    e.printStackTrace();
                }
            }

        }
        return table;
    }


    /**
     * 生成当前entity所有需要关联的表
     *
     * @return
     */
    protected List<JoinTable> joinTable() {
        List<JoinTable> list = new ArrayList<>();
        //获取所有的字段(包含父类)
        Class clazz = getClass();
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            fields.addAll(new ArrayList<Field>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        for (Field field : fields) {
            // 跳过静态属性
            String mod = Modifier.toString(field.getModifiers());
            if (mod.indexOf("static") != -1) {
                continue;
            }
            // 是否忽略当前字段的存储
            Transient transientAnnotation = field.getAnnotation(Transient.class);
            // 为空表示需要存储当前字段
            if (transientAnnotation == null || transientAnnotation.ifDataIsNull()) {
                String fieldName = field.getName();
                String joinTableName;// 表名
                String fk;//外键
                String alias = fieldName;//别名

                //查看是否定义了列属性
                Column columnAnnotation = field.getAnnotation(Column.class);
                // 如果定义了列属性,则使用定义的列属性作为fk
                if (columnAnnotation != null && StringUtils.isNotBlank(columnAnnotation.name())) {
                    fk = columnAnnotation.name();
                } else { // 否则使用当前字段名称进行驼峰转换作为column
                    fk = StringUtils.toUnderScoreCase(fieldName);
                }

                // 获取属性类型
                Class<?> typeClass = field.getType();
                //实例化
                Object typeClassInstance = null;
                try {
                    typeClassInstance = typeClass.newInstance();
                } catch (Exception e) {

                }
                // 前提: 必须是BaseEntity的子类对象,设置了Column注解,columnAnnotation为空,或者设置类column.joinTable=true
                if (typeClassInstance != null && typeClassInstance instanceof BaseEntity && (columnAnnotation == null || columnAnnotation.joinTable())) {
                    // 如果是对象类型,并且没有设置columnAnnotation.name属性,则fk应该在后面+_id
                    if (columnAnnotation == null || StringUtils.isBlank(columnAnnotation.name())) {
                        fk = fk + "_id";
                    }
                    // 获取joinTable的表明
                    Table tableAnnotation = typeClassInstance.getClass().getAnnotation(Table.class);
                    // 如果设置了table名称,则使用设置的
                    if (tableAnnotation != null && StringUtils.isNotBlank(tableAnnotation.name())) {
                        joinTableName = tableAnnotation.name();
                    } else {//如果没设置,则使用entity的类名进行驼峰转换成表名
                        joinTableName = StringUtils.toUnderScoreCase(typeClassInstance.getClass().getSimpleName());
                    }
                    // join 表中需要查询出来的字段
                    JoinTable joinTable = new JoinTable();
                    joinTable.setTableName(joinTableName);
                    joinTable.setAlias(alias);
                    joinTable.setFk(fk);

                    if (columnAnnotation != null) {
                        // 设置关联查询的关联类型
                        Column.JoinType joinType = columnAnnotation.joinType();
                        switch (joinType) {
                            case INNER_JOIN:
                                joinTable.setJoin(" INNER JOIN ");
                                break;
                            case RIGHT_JOIN:
                                joinTable.setJoin(" RIGHT JOIN ");
                                break;
                            default:
                                joinTable.setJoin(" LEFT JOIN ");

                        }
                        // 设置关联查询的附件条件
                        String[] conditions = columnAnnotation.extraConditions();
                        StringBuilder sb = new StringBuilder();
                        for (String c : conditions) {
                            sb.append(" and ").append(c);
                        }
                        if (StringUtils.isNotBlank(sb.toString())) {
                            joinTable.setConditions(sb.toString());
                        }
                        // 如果设置了自定义的外键table,则忽略 columnAnnotation.data()中的配置
                        if (columnAnnotation.fkTable().length > 0) {
                            // 这里我们只去第一个就好了,忽略后面的
                            FkTable fkTable = columnAnnotation.fkTable()[0];
                            joinTable.setFk(fkTable.columnName());
                            joinTable.setFkAlias(fkTable.alias());
                            String[] queryColumns = fkTable.queryColumns();
                            String[] queryFields = fkTable.queryFields();
                            int size = queryColumns.length <= queryFields.length ? queryColumns.length : queryFields.length;
                            for (int i = 0; i < size; i++) {
                                joinTable.getColumns().add(new AttrField(queryFields[i], queryColumns[i], null));
                            }
                        } else {
                            // 如果定义了取数据的属性,则从定义中取值,
                            String[] columnAnnotationData = columnAnnotation.data();
                            for (String c : columnAnnotationData) {
                                // 默认id的查询是从主表("a")中查询
                                if (StringUtils.endsWith(c, ".id")) {
                                    continue;
                                }
                                joinTable.getColumns().add(new AttrField(c, StringUtils.substringBeforeLast(c, ".") + "." + StringUtils.toUnderScoreCase(StringUtils.substringAfterLast(c, ".")), null));
                            }
                        }
                    }
                    list.add(joinTable);


                }
            }

        }
        return list;

    }


    /**
     * 检查数组中是否包含当前操作
     *
     * @param operates
     * @param operate
     */
    private boolean inOperatesArray(DatabaseTime.Operate[] operates, DatabaseTime.Operate operate) {
        if (operates == null || operates.length == 0) {
            return false;
        }
        for (DatabaseTime.Operate o : operates) {
            if (o.equals(operate)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 表对应关系
     */
    class AttrTable {
        private String tableName; //表名
        private String primaryColumn; // 主键列(表的列)
        private String primaryField; // 主键属性(java属性)
        private Object primaryData;// 主键值
        private List<AttrField> columns = new ArrayList<>();// 所有列

        public String getTableName() {
            return tableName;
        }

        public void setTableName(String tableName) {
            this.tableName = tableName;
        }

        public String getPrimaryColumn() {
            return primaryColumn;
        }

        public void setPrimaryColumn(String primaryColumn) {
            this.primaryColumn = primaryColumn;
        }

        public String getPrimaryField() {
            return primaryField;
        }

        public void setPrimaryField(String primaryField) {
            this.primaryField = primaryField;
        }

        public Object getPrimaryData() {
            return primaryData;
        }

        public void setPrimaryData(Object primaryData) {
            this.primaryData = primaryData;
        }

        public List<AttrField> getColumns() {
            return columns;
        }

        public void setColumns(List<AttrField> columns) {
            this.columns = columns;
        }

    }

    /**
     * 字段对应关系
     */
    class AttrField {
        private boolean ignoreAlias = false;//忽略别名(就是忽略"a.name"中 "a.")
        private String field; // 属性(java)
        private String column; // 列(数据库)
        private Object data; //数据
        private DatabaseTime.Operate[] operates;//使用数据库时间的操作

        public AttrField(String field, String column, Object data) {
            this.field = field;
            this.column = column;
            this.data = data;
        }

        public AttrField(boolean ignoreAlias, String field, String column, Object data) {
            this.ignoreAlias = ignoreAlias;
            this.field = field;
            this.column = column;
            this.data = data;
        }

        public boolean isIgnoreAlias() {
            return ignoreAlias;
        }

        public void setIgnoreAlias(boolean ignoreAlias) {
            this.ignoreAlias = ignoreAlias;
        }

        public String getField() {
            return field;
        }

        public void setField(String field) {
            this.field = field;
        }

        public String getColumn() {
            return column;
        }

        public void setColumn(String column) {
            this.column = column;
        }

        public Object getData() {
            return data;
        }

        public void setData(Object data) {
            this.data = data;
        }
        public DatabaseTime.Operate[] getOperates() {
            return operates;
        }

        public void setOperates(DatabaseTime.Operate[] operates) {
            this.operates = operates;
        }
        @Override
        public String toString() {
            return "field=" + field + ";column=" + column + ";data=" + data + "\r\n";
        }
    }

    /**
     * 关联表
     */
    class JoinTable {
        private String tableName;//关联表名
        private String join = "LEFT JOIN ";// 关联方式,默认 left join
        private String alias;// 关联表别名
        private String fk;// 外键字段
        private String fkAlias = "a";// 外键表别名,默认跟主表关联,即为: a
        private String conditions = "";//额外的过滤条件
        private List<AttrField> columns = new ArrayList<>();// 所有列

        public String getTableName() {
            return tableName;
        }

        public void setTableName(String tableName) {
            this.tableName = tableName;
        }

        public String getJoin() {
            return join;
        }

        public void setJoin(String join) {
            this.join = join;
        }

        public String getAlias() {
            return alias;
        }

        public void setAlias(String alias) {
            this.alias = alias;
        }

        public String getFk() {
            return fk;
        }

        public void setFk(String fk) {
            this.fk = fk;
        }

        public List<AttrField> getColumns() {
            return columns;
        }

        public void setColumns(List<AttrField> columns) {
            this.columns = columns;
        }

        public String getConditions() {
            return conditions;
        }

        public void setConditions(String conditions) {
            this.conditions = conditions;
        }

        public String getFkAlias() {
            return fkAlias;
        }

        public void setFkAlias(String fkAlias) {
            this.fkAlias = fkAlias;
        }

    }
}
